package qc.module.qms.service.rawdata;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.CommunicationChannelTypeEnum;
import qc.common.core.enums.qms.QmsDataFlowOperateTypeEnum;
import qc.common.core.enums.qms.QmsRawDataCalcFlagEnum;
import qc.common.core.pair.KeyValuePairDto;
import qc.common.core.utils.IdentifierUtil;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.common.core.utils.SqlRunnerUtil;
import qc.module.platform.dto.db.DBConnectionDto;
import qc.module.qms.dto.rawdata.QmsRawDataDto;
import qc.module.qms.dto.rawdata.QmsRawDataOperateResultDto;
import qc.module.qms.dto.rawdata.QmsRawDataReceiveDto;
import qc.module.qms.service.producer.QmsRawDataProducer;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * QMS原始数据记录Service，提供对原始数据进行、接收、写入、查询、修改等操作
 *
 * @author QuCheng Tech
 * @since 2025/8/18
 */
@Service
public class QmsRawDataRecordService {
    private static final Logger logger = LoggerFactory.getLogger(QmsRawDataRecordService.class);

    @Autowired
    private QmsRawDataProducer rawDataProducer;

    /**
     * 接收一条原始数据，如果发送原始数据至MQ成功由后续的原始数据保存和计算消费者进行处理，如果发送至MQ失败直接写入原始数据库表
     *
     * @param rawdata    原始采集数据
     * @param connection 原始数据库连接
     * @return qc.module.qms.dto.rawdata.QmsRawDataReceiveResultDto
     * @author QuCheng Tech
     * @since 2025/8/19
     */
    public QmsRawDataOperateResultDto receive(QmsRawDataReceiveDto rawdata, DBConnectionDto connection) {
        List<QmsRawDataReceiveDto> datas = new ArrayList<>();
        datas.add(rawdata);

        HashMap<String, DBConnectionDto> connections = new HashMap<>();
        if (rawdata != null && StringUtils.isNotBlank(rawdata.getTablecode()))
            connections.put(rawdata.getTablecode(), connection);

        return receive(datas, connections);
    }

    /**
     * 批量接收原始数据，可以为不同数据库表的原始数据，方法中自动按数据库表进行归类后处理
     * 如果发送原始数据至MQ成功由后续的原始数据保存和计算消费者进行处理，如果发送至MQ失败直接写入原始数据库表
     *
     * @param rawdatas    原始采集数据集合
     * @param connections 原始数据表编码与数据库连接对应关系集合
     * @return qc.module.qms.dto.rawdata.QmsRawDataReceiveResultDto
     * @author QuCheng Tech
     * @since 2025/8/19
     */
    public QmsRawDataOperateResultDto receive(List<QmsRawDataReceiveDto> rawdatas, HashMap<String, DBConnectionDto> connections) {
        //获取当前方法名
        logger.debug("receive");
        //初始化返回结果，默认为失败、成功条数为0
        QmsRawDataOperateResultDto result = new QmsRawDataOperateResultDto();
        result.setSuccess(false);
        result.setCount(0x0);
        result.setSuccesscount(0x0);
        result.setFailcount(0x0);

        if (rawdatas == null || rawdatas.size() < 0x1) {
            String errorMsg = "原始数据集合不能为空";

            logger.error("receive " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }

        //初始化原始数据接收处理的各数量、失败原因
        int totalCount = 0x0;
        int successCount = 0x0;
        int failCount = 0x0;
        Map<String, String> fails = new LinkedHashMap<>();

        //遍历逐条数据进行处理，按数据库表编码进行合并分类
        Map<String, List<QmsRawDataDto>> tableDatas = new HashMap<>();

        for (int i = 0; i < rawdatas.size(); i++) {
            //数据的顺序号，从1开始，用于错误提示信息中显示错误是对应第几条输入的数据
            int dataSeqIndex = i + 1;
            logger.debug("receive 遍历第[" + dataSeqIndex + "]条数据，判断数据是否有效及获取有效的数值数据");

            //先创建一个数据记录ID，后面如果获取到当前遍历的原始数据有记录ID时使用记录ID
            String rawDataId = IdentifierUtil.simpleUUID();

            QmsRawDataReceiveDto dto = rawdatas.get(i);
            //如果原始数据对象为null，不进行判断和处理，当做无效信息
            if (dto == null) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据对象不能为null；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象为null时不需要进行后续的判断处理
                continue;
            }
            //判断DTO中的信息是否有效：有效的转换为MQ中的消息
            if (StringUtils.isBlank(dto.getTablecode())) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的数据库表编码不能为空；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的数据库表编码为空时不需要进行后续的判断处理
                continue;
            }
            if (StringUtils.isBlank(dto.getStcd())) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的站点编码不能为空；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的原始站点编码为空时不需要进行后续的判断处理
                continue;
            }
            if (StringUtils.isBlank(dto.getTm())) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的采集时间不能为空；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的数据时标为空时不需要进行后续的判断处理
                continue;
            }
            //转换采集时间
            LocalDateTime tm = LocalDateTimeUtil.parse(dto.getTm());
            //测试使用不同个时间格式进行解析
            //LocalDateTime tm = LocalDateTimeUtil.parse(dto.getTm(), "yyyy-M-dd HH:mm:ss");
            //LocalDateTime tm = LocalDateTimeUtil.parse(dto.getTm(), "y-M-d H:m:s");
            if (tm == null) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的采集时间无法转换为有效的时间；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的数据时标无法转换为有效的时间时不需要进行后续的判断处理
                continue;
            }
            if (dto.getElements() == null || dto.getElements().size() < 0x1) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的采集要素和采集值集合不能为空；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的采集要素和采集值集合为空时不需要进行后续的判断处理
                continue;
            }
            //获取有效的采集要素及数据值，要求采集要素编码不能为空、采集值必须是数值（能转换为浮点型）
            //此处不转换采集值为数值型，发送至消息对内后需要做保存到数据库处理（此时不管数据类型为哪种均需要进行保存）
            //遍历采集要素和采集值集合时可能有多个错误提示信息，使用StringBuilder进行拼接出完整的错误提示信息
            StringBuilder sbRawDataElementErrorMessage = new StringBuilder();
            Map<String, String> elements = new HashMap<>();
            for (int j = 0; j < dto.getElements().size(); j++) {
                int elementSeqIndex = j + 1;
                String key = null;
                String value = null;
                //获取每项中的key作为要素编码，value作为要素值
                KeyValuePairDto item = dto.getElements().get(j);
                if (item == null) {
                    String errorMsg = "第" + dataSeqIndex + "条原始数据的第" + elementSeqIndex + "个采集要素信息为空；";
                    logger.warn("receive " + errorMsg);
                    sbRawDataElementErrorMessage.append(errorMsg);
                } else {
                    if (StringUtils.isBlank(item.getKey())) {
                        String errorMsg = "第" + dataSeqIndex + "条原始数据的第" + elementSeqIndex + "个采集要素键为空；";
                        logger.warn("receive " + errorMsg);
                        sbRawDataElementErrorMessage.append(errorMsg);
                    } else {
                        //获取采集要素编码/名称
                        key = item.getKey();
                        if (StringUtils.isBlank(item.getValue())) {
                            String errorMsg = "第" + dataSeqIndex + "条原始数据的第" + elementSeqIndex + "个采集要素值为空；";
                            logger.warn("receive " + errorMsg);
                            sbRawDataElementErrorMessage.append(errorMsg);
                        } else {
                            //此处还需要根据key判断对应的采集要素数值类型，如果是布尔、整型、浮点型才进行数值转换判断
                            //判断采集要素数值类型应在原始数据消费者中进行判断
                            value = item.getValue();
                            //添加到采集要素及数据值集合中
                            elements.put(key, value);
                        }
                    }
                }
            }
            if (elements == null || elements.size() < 0x1) {
                String errorMsg = "第" + dataSeqIndex + "条原始数据的有效采集要素和采集值集合不能为空；";
                logger.warn("receive " + errorMsg);
                fails.put(rawDataId, errorMsg);

                //原始数据对象中的获取到的采集要素和采集值集合为空时不需要进行后续的判断处理
                continue;
            } else {
                //如果在采集要素和采集值判断处理中有错误信息，使用错误信息集合进行返回
                if (sbRawDataElementErrorMessage.length() > 0x0)
                    fails.put(rawDataId, sbRawDataElementErrorMessage.toString());
            }

            //验证原始采集数据通过，设置有效数据条数+1
            totalCount++;

            //判断数据ID是否有，如果没有使用前面已经自动生成的数据ID，如果有使用原始数据对象中的ID
            if (StringUtils.isNotBlank(dto.getId()))
                rawDataId = dto.getId();

            //转换为MQ中的dto
            QmsRawDataDto msg = new QmsRawDataDto();
            msg.setId(rawDataId);
            msg.setStcd(dto.getStcd());
            msg.setTm(tm);
            //来源通道类型转换为枚举
            msg.setChannel(CommunicationChannelTypeEnum.getCommunicationChannelTypeEnum(dto.getChannel()));
            msg.setElements(elements);

            //判断是否有对应数据库表的数据记录
            String tablecode = dto.getTablecode();
            if (tableDatas.containsKey(tablecode)) {
                List<QmsRawDataDto> tableMsgs = tableDatas.get(tablecode);
                tableMsgs.add(msg);

                tableDatas.replace(tablecode, tableMsgs);
            } else {
                List<QmsRawDataDto> tableMsgs = new ArrayList<>();
                tableMsgs.add(msg);

                tableDatas.put(tablecode, tableMsgs);
            }
        }

        if (tableDatas == null || tableDatas.size() < 0x1) {
            String errorMsg = "所有原始数据中均无任何有效数据";
            logger.warn("receive " + errorMsg);
            result.setMsg(errorMsg);
            //设置返回结果中的统计数量和错误提示信息集合，设置成功数量为0，失败数量为总数
            result.setCount(totalCount);
            result.setSuccesscount(0x0);
            result.setFailcount(totalCount);
            if (fails.size() > 0x0)
                result.setFails(fails);

            return result;
        }

        //遍历逐个数据库表发送至消息队列
        for (Map.Entry<String, List<QmsRawDataDto>> entry : tableDatas.entrySet()) {
            String tablecode = entry.getKey();
            logger.debug("receive 有效的原始数值数据，数据库表[" + tablecode + "]，总数据条数[" + entry.getValue().size() + "]");
            logger.debug("ID\t站点编码\t数据时标\t通道类型\t监测要素数量");
            for (QmsRawDataDto dataItem : entry.getValue()) {
                logger.debug(dataItem.getId() + "\t" + dataItem.getStcd() + "\t" + LocalDateTimeUtil.formatDateTime(dataItem.getTm()) + "\t" + dataItem.getChannel() + "\t" + dataItem.getElements().size());
                logger.debug("监测要素编码\t数值");
                for (Map.Entry<String, String> ele : dataItem.getElements().entrySet()) {
                    logger.debug(ele.getKey() + "\t" + ele.getValue());
                }
            }
            // 同步发送，等待Broker响应
            boolean sendOrSaveToDbSuccess = false;
            String sendResult = rawDataProducer.syncSend(QmsDataFlowOperateTypeEnum.RECEIVE_RAW_DATA, tablecode, entry.getValue());
            if (StringUtils.isBlank(sendResult)) {
                sendOrSaveToDbSuccess = true;
            } else {
                //发送到MQ失败后直接保存到数据库，如果保存到数据库成也视为操作成功
                //根据数据库表编码获取对应的数据库连接信息对象
                DBConnectionDto connection = null;
                if (connections != null && connections.size() > 0x0 && connections.containsKey(tablecode))
                    connection = connections.get(tablecode);

                //调用保存原始数据到数据库方法，指定是否发送至MQ标记为false
                QmsRawDataOperateResultDto saveToDbResult = this.save(tablecode, entry.getValue(), false, connection);

                if (saveToDbResult != null && saveToDbResult.isSuccess() == true) {
                    sendOrSaveToDbSuccess = true;
                } else {
                    //发送到MQ失败，保存到数据库也失败
                    fails.put(tablecode, saveToDbResult.getMsg());
                }
            }

            //根据发送至MQ或保存至数据库是否成功对成功和失败的数量进行累加
            if (sendOrSaveToDbSuccess == true) {
                successCount += entry.getValue().size();
            } else {
                failCount += entry.getValue().size();
            }
        }

        //根据失败的数量设置是否成功，没有任意一条失败才能设置返回结果为成功
        if (failCount == 0x0)
            result.setSuccess(true);
        //设置返回结果中的统计数量和错误原因集合
        result.setCount(totalCount);
        result.setSuccesscount(successCount);
        result.setFailcount(failCount);
        if (fails.size() > 0x0)
            result.setFails(fails);

        return result;
    }

    /**
     * 原始数据保存，保存至数据库，根据数据ID判断是否存在；是否已发送至MQ的标记保持为false
     * 对传入的无效数据不进行保存，也不提示错误；需要在调用方进行数据验证及过滤处理；
     *
     * @param tablecode  数据库表编码
     * @param rawdatas   原始数据集合
     * @param connection 数据库表编码所属数据库的连接
     * @return qc.module.qms.dto.rawdata.QmsRawDataOperateResultDto
     * @author QuCheng Tech
     * @since 2025/8/19
     */
    public QmsRawDataOperateResultDto save(String tablecode, List<QmsRawDataDto> rawdatas, DBConnectionDto connection) {
        return this.save(tablecode, rawdatas, false, connection);
    }

    /**
     * 原始数据保存，保存至数据库，根据数据ID判断是否存在；
     * 对传入的无效数据不进行保存，也不提示错误；需要在调用方进行数据验证及过滤处理；
     *
     * @param tablecode   数据库表编码
     * @param rawdatas    原始数据集合
     * @param hasSendToMQ 是否已发送至MQ
     * @param connection  数据库表编码所属数据库的连接
     * @return boolean
     * @author QuCheng Tech
     * @since 2025/8/19
     */
    public QmsRawDataOperateResultDto save(String tablecode, List<QmsRawDataDto> rawdatas, boolean hasSendToMQ, DBConnectionDto connection) {
        logger.debug("save");

        //初始化返回结果，默认为失败、成功条数为0
        QmsRawDataOperateResultDto result = new QmsRawDataOperateResultDto();
        result.setSuccess(false);
        result.setCount(0x0);
        result.setSuccesscount(0x0);
        result.setFailcount(0x0);

        //要保存的原始数据集合不能为空
        if (rawdatas == null || rawdatas.size() < 0x1) {
            String errorMsg = "保存原始数据至数据库，原始数据集合不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        //判断数据库表编码不能为空、对应连接信息必须有效
        if (StringUtils.isBlank(tablecode)) {
            String errorMsg = "保存原始数据至数据库，数据库表编码不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (connection == null) {
            String errorMsg = "保存原始数据至数据库，数据库表编码[" + tablecode + "]对应的数据库连接信息不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        String url = connection.getUrl();
        String user = connection.getUser();
        String password = connection.getPassword();
        if (StringUtils.isBlank(url)) {
            String errorMsg = "保存原始数据至数据库，数据库表编码[" + tablecode + "]对应的数据库连接中url不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (StringUtils.isBlank(user)) {
            String errorMsg = "保存原始数据至数据库，数据库表编码[" + tablecode + "]对应的数据库连接中用户名不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (StringUtils.isBlank(password)) {
            String errorMsg = "保存原始数据至数据库，数据库表编码[" + tablecode + "]对应的数据库连接中密码不能为空";

            logger.error("save " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }

        //初始化原始数据接收处理的各数量、失败原因
        int totalCount = 0x0;
        int successCount = 0x0;
        int failCount = 0x0;
        Map<String, String> fails = new LinkedHashMap<>();

        for (QmsRawDataDto rawdata : rawdatas) {
            //如果原始数据对象为null当做无效处理，不提示
            if (rawdata != null) {
                totalCount++;
                //写入数据至数据库，固定使用的列名：id、stcd、tm、chl、flag、uptm
                Map<String, Object> columnDatas = new HashMap<>();
                //原始数据中的id、站点编码、数据时标、通道类型、采集要素和对应值
                String id = rawdata.getId();
                if (StringUtils.isBlank(id))
                    id = IdentifierUtil.randomUUID();//没有ID时自动生成一个ID
                columnDatas.put("id", id);
                columnDatas.put("stcd", rawdata.getStcd());
                columnDatas.put("tm", rawdata.getTm());
                columnDatas.put("chl", rawdata.getChannel().getIndex());
                //固定处理标记为0或1，写入时间为now
                if (hasSendToMQ == true)
                    columnDatas.put("flag", QmsRawDataCalcFlagEnum.SENDED.getIndex());
                else
                    columnDatas.put("flag", QmsRawDataCalcFlagEnum.UN_DEFINE.getIndex());
                columnDatas.put("intm", LocalDateTimeUtil.getNow());

                //遍历采集要素和值集合
                Map<String, String> elements = rawdata.getElements();
                if (elements != null && elements.size() > 0x0) {
                    for (Map.Entry<String, String> entry : elements.entrySet()) {
                        columnDatas.put(entry.getKey(), entry.getValue());
                    }
                }

                try {
                    boolean insertToDbSuccess = SqlRunnerUtil.insertData(url, user, password, tablecode, columnDatas);
                    if (insertToDbSuccess == true) {
                        successCount++;

                        String successMsg = "保存原始数据至数据库，保存成功，记录ID[" + id + "]";
                        logger.info("save " + successMsg);
                    } else {
                        failCount++;
                        //写入数据库失败，其中任意一条原始数据保存失败时返回false表示保存失败
                        String errorMsg = "保存原始数据至数据库，保存失败，记录ID[" + id + "]";
                        logger.error("save " + errorMsg);
                        fails.put(id, errorMsg);
                    }
                } catch (SQLException ex) {
                    failCount++;
                    //写入数据库异常，其中任意一条原始数据保存失败时返回false表示保存失败
                    String errorMsg = "保存原始数据至数据库，保存时异常，记录ID[" + id + "]，异常信息[" + ex.getMessage() + "]";
                    logger.error("save " + errorMsg);
                    fails.put(id, errorMsg);
                }
            }
        }

        //根据失败的数量设置是否成功，没有任意一条失败才能设置返回结果为成功
        if (failCount == 0x0)
            result.setSuccess(true);
        if (successCount < totalCount) {
            //如果总数只有1条，直接显示失败的信息；如果总数＞1条，显示成功和失败的数量
            if (totalCount == 0x1 && fails != null && fails.size() == 0x1) {
                for (Map.Entry<String, String> failEntry : fails.entrySet()) {
                    result.setMsg(failEntry.getValue());
                }
            } else {
                //如果有保存失败的，在提示信息中显示成功和失败的数量
                result.setMsg("共有数据" + totalCount + "条，保存成功" + successCount + "条，保存失败" + failCount + "条。");
            }
        }
        //设置返回结果中的统计数量和错误原因集合
        result.setCount(totalCount);
        result.setSuccesscount(successCount);
        result.setFailcount(failCount);
        if (fails.size() > 0x0)
            result.setFails(fails);

        return result;
    }

    /**
     * 设置原始数据的处理标记，用于原始数据计算处理后调用；设置计算标记同时判断如果数据库中不存在时进行写入；
     * 待定问题：是否需要针对不同监测要素（列）进行分别设置处理标记，暂时不考虑分不同监测要素设置不同的处理标记
     *
     * @param tablecode  数据库表编码
     * @param ele        监测要素编码，对应列名
     * @param rawdatas   原始数据集合
     * @param flag       处理标记
     * @param connection 数据库表编码所属数据库的连接
     * @return qc.module.qms.dto.rawdata.QmsRawDataOperateResultDto
     * @author QuCheng Tech
     * @since 2025/11/3
     */
    public QmsRawDataOperateResultDto setFlag(String tablecode, String ele, List<QmsRawDataDto> rawdatas, QmsRawDataCalcFlagEnum flag, DBConnectionDto connection) {
        //初始化返回结果，默认为失败、成功条数为0
        QmsRawDataOperateResultDto result = new QmsRawDataOperateResultDto();
        result.setSuccess(false);
        result.setCount(0x0);
        result.setSuccesscount(0x0);
        result.setFailcount(0x0);

        //要保存的原始数据集合不能为空
        if (rawdatas == null || rawdatas.size() < 0x1) {
            String errorMsg = "修改原始数据处理标记，原始数据集合不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        //判断数据库表编码不能为空、对应连接信息必须有效
        if (StringUtils.isBlank(tablecode)) {
            String errorMsg = "保存原始数据至数据库，数据库表编码不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (connection == null) {
            String errorMsg = "修改原始数据处理标记，数据库表编码[" + tablecode + "]对应的数据库连接信息不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        String url = connection.getUrl();
        String user = connection.getUser();
        String password = connection.getPassword();
        if (StringUtils.isBlank(url)) {
            String errorMsg = "修改原始数据处理标记，数据库表编码[" + tablecode + "]对应的数据库连接中url不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (StringUtils.isBlank(user)) {
            String errorMsg = "修改原始数据处理标记，数据库表编码[" + tablecode + "]对应的数据库连接中用户名不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }
        if (StringUtils.isBlank(password)) {
            String errorMsg = "修改原始数据处理标记，数据库表编码[" + tablecode + "]对应的数据库连接中密码不能为空";

            logger.error("setFlag " + errorMsg);
            result.setMsg(errorMsg);
            return result;
        }

        //初始化原始数据接收处理的各数量、失败原因
        int totalCount = 0x0;
        int successCount = 0x0;
        int failCount = 0x0;
        Map<String, String> fails = new LinkedHashMap<>();

        for (QmsRawDataDto rawdata : rawdatas) {
            //如果原始数据对象为null、数据ID为空当做无效处理，不提示
            //数据ID必须有，根据数据ID进行修改标记
            if (rawdata != null && StringUtils.isNotBlank(rawdata.getId())) {
                String id = rawdata.getId();
                totalCount++;

                LocalDateTime now = LocalDateTimeUtil.getNow();
                //逐条原始数据记录进行处理：先根据记录ID判断数据是否存在，存在时进行update，不存在时进行insert(同时对处理标记进行赋值)
                Map<String, Object> hasExistWhereDatas = new HashMap<>();
                hasExistWhereDatas.put("id", id);
                try {
                    boolean hasExist = SqlRunnerUtil.hasExistData(url, user, password, tablecode, hasExistWhereDatas);
                    if (hasExist == true) {
                        //存在记录ID为当前原始数据的记录，进行update
                        //String sql = "UPDATE " + tablecode + " SET flag = ?,uptm = ? WHERE id = ?";
                        //修改处理标记SQL语句，修改时使用的条件：id相等、flag要求不为空或未定义（未发送至MQ的不修改处理标记）
                        String sql = "UPDATE " + tablecode + " SET flag = ?,uptm = ? WHERE id = ? AND flag > ? ";
                        try {
                            boolean setFlagSuccess = SqlRunnerUtil.executeUpdateSql(url, user, password, sql, flag.getIndex(), now, id, QmsRawDataCalcFlagEnum.UN_DEFINE.getIndex());
                            //boolean setFlagSuccess = SqlRunnerUtil.executeUpdateSql(url, user, password, sql, flag.getIndex(), id, QmsRawDataCalcFlagEnum.UN_DEFINE.getIndex());
                            if (setFlagSuccess == true) {
                                successCount++;

                                String successMsg = "修改原始数据处理标记，修改成功，记录ID[" + id + "]";
                                logger.info("setFlag " + successMsg);
                            } else {
                                failCount++;
                                //写入数据库失败，其中任意一条原始数据保存失败时返回false表示保存失败
                                String errorMsg = "修改原始数据处理标记，修改失败，记录ID[" + id + "]";
                                logger.error("setFlag " + errorMsg);
                                fails.put(id, errorMsg);
                            }
                        } catch (SQLException ex) {
                            failCount++;
                            //写入数据库异常，其中任意一条原始数据保存失败时返回false表示保存失败
                            String errorMsg = "修改原始数据处理标记，修改时异常，记录ID[" + id + "]，异常信息[" + ex.getMessage() + "]";
                            logger.error("setFlag " + errorMsg);
                            fails.put(id, errorMsg);
                        }
                    } else {
                        //表示指定id的记录不存在，在此进行数据的新增；固定使用的列名：id、stcd、tm、chl、flag、uptm
                        Map<String, Object> columnDatas = new HashMap<>();
                        //原始数据中的id、站点编码、数据时标、通道类型、采集要素和对应值
                        columnDatas.put("id", id);
                        columnDatas.put("stcd", rawdata.getStcd());
                        columnDatas.put("tm", rawdata.getTm());
                        columnDatas.put("chl", rawdata.getChannel().getIndex());
                        //固定处理标记为传入的标记值，写入时间为now
                        columnDatas.put("flag", flag.getIndex());
                        columnDatas.put("intm", now);
                        columnDatas.put("uptm", now);

                        //遍历采集要素和值集合
                        Map<String, String> elements = rawdata.getElements();
                        if (elements != null && elements.size() > 0x0) {
                            for (Map.Entry<String, String> entry : elements.entrySet()) {
                                columnDatas.put(entry.getKey(), entry.getValue());
                            }
                        }

                        try {
                            boolean insertToDbSuccess = SqlRunnerUtil.insertData(url, user, password, tablecode, columnDatas);
                            if (insertToDbSuccess == true) {
                                successCount++;

                                String successMsg = "修改原始数据处理标记，记录不存在本次新增记录保存成功，记录ID[" + id + "]";
                                logger.info("setFlag " + successMsg);
                            } else {
                                failCount++;
                                //写入数据库失败，其中任意一条原始数据保存失败时返回false表示保存失败
                                String errorMsg = "修改原始数据处理标记，记录不存在本次新增记录保存失败，记录ID[" + id + "]";
                                logger.error("setFlag " + errorMsg);
                                fails.put(id, errorMsg);
                            }
                        } catch (SQLException ex) {
                            failCount++;
                            //写入数据库异常，其中任意一条原始数据保存失败时返回false表示保存失败
                            String errorMsg = "保存原始数据至数据库，记录不存在本次新增记录保存时异常，记录ID[" + id + "]，异常信息[" + ex.getMessage() + "]";
                            logger.error("setFlag " + errorMsg);
                            fails.put(id, errorMsg);
                        }
                    }
                } catch (SQLException ex) {
                    failCount++;
                    //判断记录是否存在时异常
                    String errorMsg = "修改原始数据处理标记，判断记录是否存在时异常，记录ID[" + id + "]，异常信息[" + ex.getMessage() + "]";
                    logger.error("setFlag " + errorMsg);
                    fails.put(id, errorMsg);
                }
            }
        }

        //根据失败的数量设置是否成功，没有任意一条失败才能设置返回结果为成功
        if (failCount == 0x0)
            result.setSuccess(true);
        //设置返回结果中的统计数量和错误原因集合
        result.setCount(totalCount);
        result.setSuccesscount(successCount);
        result.setFailcount(failCount);
        if (fails.size() > 0x0)
            result.setFails(fails);

        return result;
    }

    //重新处理原始数据，取出后发送至MQ
    //人工操作：重新计算；stcd和ele均可以为空，时间段可以是写入时间或者数据时标
    //修改数据库中的计算标记，并将已有数据发送至消息队列
    public QmsRawDataOperateResultDto reSend(String tablecode, String stcd, String ele, boolean isIntm, LocalDateTime beginTime, LocalDateTime endTime, DBConnectionDto connection) {
        //初始化返回结果，默认为失败、成功条数为0
        QmsRawDataOperateResultDto result = new QmsRawDataOperateResultDto();
        result.setSuccess(false);
        result.setCount(0x0);
        result.setSuccesscount(0x0);
        result.setFailcount(0x0);

        //初始化原始数据接收处理的各数量、失败原因
        int totalCount = 0x0;
        int successCount = 0x0;
        int failCount = 0x0;
        Map<String, String> fails = new LinkedHashMap<>();

        //根据失败的数量设置是否成功，没有任意一条失败才能设置返回结果为成功
        if (failCount == 0x0)
            result.setSuccess(true);
        //设置返回结果中的统计数量和错误原因集合
        result.setCount(totalCount);
        result.setSuccesscount(successCount);
        result.setFailcount(failCount);
        if (fails.size() > 0x0)
            result.setFails(fails);

        return result;
    }

    //一般情况下不提供删除；作为后台管理模块使用
    public QmsRawDataOperateResultDto delete(String tablecode, Date date) {
        //初始化返回结果，默认为失败、成功条数为0
        QmsRawDataOperateResultDto result = new QmsRawDataOperateResultDto();
        result.setSuccess(false);
        result.setCount(0x0);
        result.setSuccesscount(0x0);
        result.setFailcount(0x0);

        //初始化原始数据接收处理的各数量、失败原因
        int totalCount = 0x0;
        int successCount = 0x0;
        int failCount = 0x0;
        Map<String, String> fails = new LinkedHashMap<>();

        //根据失败的数量设置是否成功，没有任意一条失败才能设置返回结果为成功
        if (failCount == 0x0)
            result.setSuccess(true);
        //设置返回结果中的统计数量和错误原因集合
        result.setCount(totalCount);
        result.setSuccesscount(successCount);
        result.setFailcount(failCount);
        if (fails.size() > 0x0)
            result.setFails(fails);

        return result;
    }

    /**
     * 获取指定数据库表中指定站点编码和列名的一段时间内的原始数据集合，只返回数据时标和数据值集合，按数据时标升序
     * 用于在原始数据计算时数据为累计值需要对起止时间段内的全部数据进行重新计算，需要提取起止时间段内的全部原始数据
     *
     * @param tableCode  数据库表编码
     * @param stcd       站点编码
     * @param columnCode 列名
     * @param beginTime  起始时间，包含该时间点
     * @param endTime    截止时间，包含该时间点
     * @return java.util.Map<java.util.Date, java.lang.Double>
     * @author QuCheng Tech
     * @since 2025/9/9
     */
    public Map<LocalDateTime, Double> getStationElementRawDatas(String tableCode, String stcd, String columnCode, LocalDateTime beginTime, LocalDateTime endTime) {
        logger.debug("getStationElementRawDatas");
        //构造查询sql
        String sql = "select tm," + columnCode + " from " + tableCode + " where stcd = " + stcd + " and tm between " + beginTime + " and " + endTime + " order by tm asc";
        logger.debug(sql);
        //如果查询结果中的第1条数据时标不是开始时间，需要查询前1条最新数据

        return null;
    }
}
